home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / gnu / diff_2_1.lha / diff-2.1 / sdiff.c < prev    next >
C/C++ Source or Header  |  1993-02-03  |  28KB  |  1,366 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord. */
  21.  
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include "system.h"
  25. #include <signal.h>
  26. #include "getopt.h"
  27.  
  28. #ifdef AMIGA
  29. #include <exec/types.h>
  30. #include <dos/dostags.h>
  31. #include <proto/exec.h>
  32. #include <proto/dos.h>
  33.  
  34. extern struct DosLibrary *DOSBase;
  35. #endif /* AMIGA */
  36.  
  37. #ifndef SEEK_SET
  38. #define SEEK_SET 0
  39. #endif
  40.  
  41. /* Size of chunks read from files which must be parsed into lines. */
  42. #define SDIFF_BUFSIZE 65536
  43.  
  44. /* Default name of the diff program */
  45. #ifndef DIFF_PROGRAM
  46. #define DIFF_PROGRAM "/usr/bin/diff"
  47. #endif
  48.  
  49. /* Users' editor of nonchoice */
  50. #ifndef DEFAULT_EDITOR
  51. #define DEFAULT_EDITOR "ed"
  52. #endif
  53.  
  54. extern char *version_string;
  55. static char const *prog;
  56. static char const *diffbin = DIFF_PROGRAM;
  57. static char const *edbin = DEFAULT_EDITOR;
  58.  
  59. static char *tmpname;
  60. static int volatile tmpmade;
  61. #ifndef AMIGA
  62. static pid_t volatile diffpid;
  63. #endif /* !AMIGA */
  64.  
  65. struct line_filter;
  66. static void diffarg (); /* (char *); */
  67. static void execdiff (); /* (int, char const *, char const *, char const *); */
  68. #ifdef AMIGA
  69. char *xmalloc ();
  70. static void amiga_exit ();
  71. static int amiga_break ();
  72. static void construct_pipe_name ();
  73. static void construct_command_line ();
  74. static char command_line[512];
  75. static FILE *diff_file = NULL;
  76. static int user_quit = 0;
  77. #endif /* AMIGA */
  78. static int edit (); /* (struct line_filter *left, int lenl, struct
  79.                line_filter *right, int lenr, FILE *outfile); */
  80. static int interact (); /* (struct line_filter *diff,
  81.               struct line_filter *left,
  82.               struct line_filter *right, FILE *outfile); */
  83. #ifndef AMIGA
  84. static void trapsigs (); /* (void); */
  85. /* this lossage until the gnu libc conquers the universe */
  86. #define TMPNAMSIZE 1024
  87. #define PVT_tmpdir "/tmp"
  88. static char *private_tempnam (); /* (const char *, const char *, int, int *); */
  89. #endif /* !AMIGA */
  90.  
  91. static int diraccess ();
  92.  
  93. /* Options: */
  94.  
  95. /* name of output file if -o spec'd */
  96. static char *out_file;
  97.  
  98. /* do not print common lines if true, set by -s option */
  99. static int suppress_common_flag;
  100.  
  101. static struct option longopts[] =
  102. {
  103.   {"ignore-blank-lines", 0, NULL, 'B'},
  104.   {"speed-large-files", 0, NULL, 'H'},
  105.   {"ignore-matching-lines", 1, NULL, 'I'},
  106.   {"ignore-all-space", 0, NULL, 'W'}, /* swap W and w for historical reasons */
  107.   {"text", 0, NULL, 'a'},
  108.   {"ignore-space-change", 0, NULL, 'b'},
  109.   {"minimal", 0, NULL, 'd'},
  110.   {"ignore-case", 0, NULL, 'i'},
  111.   {"left-column", 0, NULL, 'l'},
  112.   {"output", 1, NULL, 'o'},
  113.   {"suppress-common-lines", 0, NULL, 's'},
  114.   {"expand-tabs", 0, NULL, 't'},
  115.   {"width", 1, NULL, 'w'},
  116.   {"version", 0, NULL, 'v'},
  117.   {NULL, 0, NULL, 0}
  118. };
  119.  
  120. /* prints usage message and quits */
  121. static void
  122. usage ()
  123. {
  124.   fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
  125.   fprintf (stderr, "Options:\n\
  126.        [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
  127.        [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
  128.        [--ignore-case] [--ignore-matching-lines=regexp]\n\
  129.        [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
  130.        [--suppress-common-lines] [--left-column] [--output=outfile]\n\
  131.        [--version] [--width=columns]\n");
  132.   exit (2);
  133. }
  134.  
  135. #ifdef AMIGA
  136. static void
  137. amiga_exit ()
  138. {
  139.   char *buf;
  140.   size_t a;
  141.  
  142.   if (tmpmade)
  143.     {
  144.       remove (tmpname);
  145.       tmpmade = 0;
  146.     }
  147.   if (diff_file)
  148.     {
  149.       /* Provide empty pipe! */
  150.       buf = xmalloc (SDIFF_BUFSIZE);
  151.       do
  152.         a = fread (buf, sizeof (char), SDIFF_BUFSIZE, diff_file);
  153.       while (a == SDIFF_BUFSIZE);
  154.       fclose (diff_file);
  155.       diff_file = NULL;
  156.       free (buf);
  157.     }
  158. }
  159.  
  160. static int
  161. amiga_break ()
  162. {
  163.   amiga_exit ();
  164.   return 20;
  165. }
  166. #endif /* AMIGA */
  167.  
  168. static void
  169. cleanup ()
  170. {
  171. #ifndef AMIGA
  172.   if (0 < diffpid)
  173.     kill (diffpid, SIGPIPE);
  174.   if (tmpmade)
  175.     unlink (tmpname);
  176. #else /* AMIGA */
  177.   if (tmpmade)
  178.     {
  179.       remove (tmpname);
  180.       tmpmade = 0;
  181.     }
  182. #endif /* !AMIGA */
  183. }
  184.  
  185. static void
  186. exiterr ()
  187. {
  188.   cleanup ();
  189.   exit (2);
  190. }
  191.  
  192. static void
  193. fatal (msg)
  194.      char *msg;
  195. {
  196.   fprintf (stderr, "%s: %s\n", prog, msg);
  197.   exiterr ();
  198. }
  199.  
  200. static void
  201. perror_fatal (msg)
  202.      char *msg;
  203. {
  204.   int e = errno;
  205.   fprintf (stderr, "%s: ", prog);
  206.   errno = e;
  207.   perror (msg);
  208.   exiterr ();
  209. }
  210.  
  211.  
  212. /* malloc freely or DIE! */
  213. char *
  214. xmalloc (size)
  215.      size_t size;
  216. {
  217.   char *r = malloc (size);
  218.   if (!r)
  219.     fatal ("virtual memory exhausted");
  220.   return r;
  221. }
  222.  
  223. static FILE *
  224. ck_fopen (fname, type)
  225.      char *fname, *type;
  226. {
  227.   FILE *r = fopen (fname, type);
  228.   if (!r)
  229.     perror_fatal (fname);
  230.   return r;
  231. }
  232.  
  233.  
  234. static FILE *
  235. ck_fdopen (fd, type)
  236.      int fd;
  237.      char *type;
  238. {
  239.   FILE *r = fdopen (fd, type);
  240.   if (!r)
  241.     perror_fatal ("fdopen");
  242.   return r;
  243. }
  244.  
  245. static void
  246. ck_fclose (f)
  247.      FILE *f;
  248. {
  249.   if (fclose (f))
  250.     perror_fatal ("input/output error");
  251. }
  252.  
  253. static size_t
  254. ck_fread (buf, size, f)
  255.      char *buf;
  256.      size_t size;
  257.      FILE *f;
  258. {
  259.   size_t r = fread (buf, sizeof (char), size, f);
  260.   if (r == 0 && ferror (f))
  261.     perror_fatal ("input error");
  262.   return r;
  263. }
  264.  
  265. static void
  266. ck_fwrite (buf, size, f)
  267.      char *buf;
  268.      size_t size;
  269.      FILE *f;
  270. {
  271.   if (fwrite (buf, sizeof (char), size, f) != size)
  272.     perror_fatal ("output error");
  273. }
  274.  
  275. static void
  276. ck_fflush (f)
  277.      FILE *f;
  278. {
  279.   if (fflush (f) != 0)
  280.     perror_fatal ("output error");
  281. }
  282.  
  283. #if !HAVE_MEMCHR
  284. char *
  285. memchr (s, c, n)
  286.      char *s;
  287.      int c;
  288.      size_t n;
  289. {
  290.   unsigned char *p = (unsigned char *) s, *lim = p + n;
  291.   for (;  p < lim;  p++)
  292.     if (*p == c)
  293.       return (char *) p;
  294.   return 0;
  295. }
  296. #endif
  297.  
  298. #ifndef AMIGA
  299. #ifndef HAVE_WAITPID
  300. /* Emulate waitpid well enough for sdiff, which has at most two children.  */
  301. static pid_t
  302. waitpid (pid, stat_loc, options)
  303.      pid_t pid;
  304.      int *stat_loc;
  305.      int options;
  306. {
  307.   static int ostatus;
  308.   static pid_t opid;
  309.   int npid, status;
  310.  
  311.   if (pid == opid)
  312.     {
  313.       opid = 0;
  314.       status = ostatus;
  315.     }
  316.   else
  317.     while ((npid = wait (&status)) != pid)
  318.       {
  319.     if (npid < 0)
  320.       return npid;
  321.     opid = npid;
  322.     ostatus = status;
  323.       }
  324.   *stat_loc = status;
  325.   return pid;
  326. }
  327. #endif
  328. #endif /* !AMIGA */
  329.  
  330. static char const *
  331. expand_name (name, isdir, other_name)
  332.      char *name;
  333.      int isdir;
  334.      char const *other_name;
  335. {
  336.   if (strcmp (name, "-") == 0)
  337.     fatal ("cannot interactively merge standard input");
  338.   if (!isdir)
  339.     return name;
  340.   else
  341.     {
  342.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  343. #ifndef AMIGA
  344.       const char
  345.     *p = rindex (other_name, '/'),
  346.     *base = p ? p+1 : other_name;
  347.       size_t namelen = strlen (name), baselen = strlen (base);
  348.       char *r = xmalloc (namelen + baselen + 2);
  349.       bcopy (name, r, namelen);
  350.       r[namelen] = '/';
  351.       bcopy (base, r + namelen + 1, baselen + 1);
  352.       return r;
  353. #else /* AMIGA */
  354.       const char *p1, *p2, *base;
  355.       size_t namelen, baselen;
  356.       char *r;
  357.  
  358.       p1 = rindex (other_name, '/');
  359.       p2 = rindex (other_name, ':');
  360.       if (p1 == NULL && p2 == NULL)
  361.         base = other_name;
  362.       else
  363.         base = max (p1 + 1 , p2 + 1);
  364.       namelen = strlen (name);
  365.       baselen = strlen (base);
  366.       r = xmalloc (namelen + baselen + 2);
  367.       bcopy (name, r, namelen);
  368.       if (name[namelen-1] != ':')
  369.         {
  370.           r[namelen] = '/';
  371.           bcopy (base, r + namelen + 1, baselen + 1);
  372.         }
  373.       else
  374.         {
  375.           bcopy (base, r + namelen, baselen + 1);
  376.         }
  377.       return r;
  378. #endif /* AMIGA */
  379.     }
  380. }
  381.  
  382.  
  383.  
  384. struct line_filter {
  385.   FILE *infile;
  386.   char *bufpos;
  387.   char *buffer;
  388.   char *buflim;
  389. };
  390.  
  391. static void
  392. lf_init (lf, infile)
  393.      struct line_filter *lf;
  394.      FILE *infile;
  395. {
  396.   lf->infile = infile;
  397.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  398.   lf->buflim[0] = '\n';
  399. }
  400.  
  401. /* Fill an exhausted line_filter buffer from its INFILE */
  402. static size_t
  403. lf_refill (lf)
  404.      struct line_filter *lf;
  405. {
  406.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  407.   lf->bufpos = lf->buffer;
  408.   lf->buflim = lf->buffer + s;
  409.   lf->buflim[0] = '\n';
  410.   return s;
  411. }
  412.  
  413. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  414. static void
  415. lf_copy (lf, lines, outfile)
  416.      struct line_filter *lf;
  417.      int lines;
  418.      FILE *outfile;
  419. {
  420.   char *start = lf->bufpos;
  421.  
  422.   while (lines)
  423.     {
  424.       lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  425.       if (! lf->bufpos)
  426.     {
  427.       ck_fwrite (start, lf->buflim - start, outfile);
  428.       if (! lf_refill (lf))
  429.         return;
  430.       start = lf->bufpos;
  431.     }
  432.       else
  433.     {
  434.       --lines;
  435.       ++lf->bufpos;
  436.     }
  437.     }
  438.  
  439.   ck_fwrite (start, lf->bufpos - start, outfile);
  440. }
  441.  
  442. /* Advance LINES on LF's infile without doing output */
  443. static void
  444. lf_skip (lf, lines)
  445.      struct line_filter *lf;
  446.      int lines;
  447. {
  448.   while (lines)
  449.     {
  450.       lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  451.       if (! lf->bufpos)
  452.     {
  453.       if (! lf_refill (lf))
  454.         break;
  455.     }
  456.       else
  457.     {
  458.       --lines;
  459.       ++lf->bufpos;
  460.     }
  461.     }
  462. }
  463.  
  464. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  465. static int
  466. lf_snarf (lf, buffer, bufsize)
  467.      struct line_filter *lf;
  468.      char *buffer;
  469.      size_t bufsize;
  470. {
  471.   char *start = lf->bufpos;
  472.  
  473.   for (;;)
  474.     {
  475.       char *next = memchr (start, '\n', lf->buflim + 1 - start);
  476.       size_t s = next - start;
  477.       if (bufsize <= s)
  478.     return 0;
  479.       bcopy (start, buffer, s);
  480.       if (next < lf->buflim)
  481.     {
  482.       buffer[s] = 0;
  483.       lf->bufpos = next + 1;
  484.       return 1;
  485.     }
  486.       if (! lf_refill (lf))
  487.     return s ? 0 : EOF;
  488.       buffer += s;
  489.       bufsize -= s;
  490.       start = next;
  491.     }
  492. }
  493.  
  494.  
  495.  
  496. int
  497. main (argc, argv)
  498.      int argc;
  499.      char *argv[];
  500. {
  501.   int opt;
  502.   int version_requested = 0;
  503.   char *editor = getenv ("EDITOR");
  504.   char *differ = getenv ("DIFF");
  505.  
  506. #ifdef AMIGA
  507.   if (DOSBase->dl_lib.lib_Version < 37) {
  508.     fputs ("Need Amiga OS 2.0 (V.37) to execute.\n", stderr);
  509.     exit (20);
  510.   }
  511.   /* Install break and exit traps */
  512.   if (atexit (&amiga_exit))
  513.     fatal ("couldn't set exit trap");
  514.   if (onbreak (&amiga_break))
  515.     fatal ("couldn't set break trap");
  516. #endif /* AMIGA */
  517.  
  518.   prog = argv[0];
  519.   if (editor)
  520.     edbin = editor;
  521.   if (differ)
  522.     diffbin = differ;
  523.  
  524.   diffarg ("diff");
  525.  
  526.   /* parse command line args */
  527.   while ((opt=getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, (int *)0)) != EOF)
  528.     {
  529.       switch (opt)
  530.     {
  531.     case 'a':
  532.       diffarg ("-a");
  533.       break;
  534.  
  535.     case 'b':
  536.       diffarg ("-b");
  537.       break;
  538.  
  539.     case 'B':
  540.       diffarg ("-B");
  541.       break;
  542.  
  543.     case 'd':
  544.       diffarg ("-d");
  545.       break;
  546.  
  547.     case 'H':
  548.       diffarg ("-H");
  549.       break;
  550.  
  551.     case 'i':
  552.       diffarg ("-i");
  553.       break;
  554.  
  555.     case 'I':
  556.       diffarg ("-I");
  557.       diffarg (optarg);
  558.       break;
  559.  
  560.     case 'l':
  561.       diffarg ("--left-column");
  562.       break;
  563.  
  564.     case 'o':
  565.       out_file = optarg;
  566.       break;
  567.  
  568.     case 's':
  569.       suppress_common_flag = 1;
  570.       break;
  571.  
  572.     case 't':
  573.       diffarg ("-t");
  574.       break;
  575.  
  576.     case 'v':
  577.       version_requested = 1;
  578.       fprintf (stderr, "GNU sdiff version %s\n", version_string);
  579.       ck_fflush (stderr);
  580.       break;
  581.  
  582.     case 'w':
  583.       diffarg ("-W");
  584.       diffarg (optarg);
  585.       break;
  586.  
  587.     case 'W':
  588.       diffarg ("-w");
  589.       break;
  590.  
  591.     default:
  592.       usage ();
  593.     }
  594.     }
  595.  
  596.   /* check: did user just want version message? if so exit. */
  597.   if (version_requested && argc - optind == 0)
  598.     exit (0);
  599.  
  600.   if (argc - optind != 2)
  601.     usage ();
  602.  
  603.   if (! out_file)
  604.     /* easy case: diff does everything for us */
  605. #ifndef AMIGA
  606.     execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
  607. #else /* AMIGA */
  608.     execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1], FALSE, NULL);
  609. #endif /* !AMIGA */
  610.   else
  611.     {
  612.       FILE *left, *right, *out, *diffout;
  613.       int diff_fds[2];
  614.       int interact_ok;
  615. #ifndef AMIGA
  616.       pid_t pid;
  617. #endif
  618.       struct line_filter lfilt;
  619.       struct line_filter rfilt;
  620.       struct line_filter diff_filt;
  621.       int leftdir = diraccess (argv[optind]);
  622.       int rightdir = diraccess (argv[optind + 1]);
  623.  
  624.       if (leftdir && rightdir)
  625.     fatal ("both files to be compared are directories");
  626.  
  627.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  628.       ;
  629.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  630.       out = ck_fopen (out_file, "w");
  631.  
  632. #ifndef AMIGA
  633.  
  634.       if (pipe (diff_fds))
  635.     perror_fatal ("pipe");
  636.  
  637.       trapsigs ();
  638.  
  639.       diffpid = pid = vfork ();
  640.  
  641.       if (pid == 0)
  642.     {
  643.       signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  644.       signal (SIGPIPE, SIG_DFL);
  645.  
  646.       close (diff_fds[0]);
  647.       if (diff_fds[1] != fileno (stdout))
  648.         {
  649.           dup2 (diff_fds[1], fileno (stdout));
  650.           close (diff_fds[1]);
  651.         }
  652.  
  653.       execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
  654.     }
  655.  
  656.       if (pid < 0)
  657.     perror_fatal ("fork failed");
  658.  
  659.       close (diff_fds[1]);
  660.       diffout = ck_fdopen (diff_fds[0], "r");
  661.  
  662. #else /* AMIGA */
  663.  
  664.       {
  665.         BPTR StdOutDiff;
  666.         char pipe_name[20];
  667.  
  668.         construct_pipe_name (pipe_name);
  669.  
  670.         StdOutDiff = Open (pipe_name, MODE_NEWFILE);
  671.         if (!StdOutDiff)
  672.           perror_fatal ("pipe");
  673.  
  674.         diff_fds[0] = open (pipe_name, O_RDONLY);
  675.         if (diff_fds[0] == -1)
  676.           perror_fatal ("pipe");
  677.  
  678.         execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1], TRUE, StdOutDiff);
  679.  
  680.       }
  681.  
  682.       diffout = ck_fdopen (diff_fds[0], "r");
  683.       diff_file = diffout;
  684.  
  685. #endif /* !AMIGA */
  686.  
  687.       lf_init (&diff_filt, diffout);
  688.       lf_init (&lfilt, left);
  689.       lf_init (&rfilt, right);
  690.  
  691.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  692.  
  693. #ifndef AMIGA
  694.       ck_fclose (diffout);
  695. #else /* AMIGA */
  696.       /* If the user signaled quit, let the exit code clean up the
  697.          pipe and close the file */
  698.       if (!user_quit)
  699.         ck_fclose (diffout);
  700. #endif /* !AMIGA */
  701.       ck_fclose (left);
  702.       ck_fclose (right);
  703.       ck_fclose (out);
  704.  
  705.       {
  706. #ifndef AMIGA
  707.     int wstatus;
  708.  
  709.     if (waitpid (pid, &wstatus, 0) < 0)
  710.       perror_fatal ("wait failed");
  711.     diffpid = 0;
  712.  
  713.     if (tmpmade)
  714.       {
  715.         unlink (tmpname);
  716.         tmpmade = 0;
  717.       }
  718.  
  719.     if (! interact_ok)
  720.       exit (2);
  721.  
  722.     if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  723.       fatal ("Subsidiary diff failed");
  724.  
  725.     exit (WEXITSTATUS (wstatus));
  726. #else /* AMIGA */
  727.     if (tmpmade)
  728.       {
  729.         remove (tmpname);
  730.         tmpmade = 0;
  731.       }
  732.  
  733.     if (! interact_ok)
  734.       exit (2);
  735.  
  736.         diff_file = NULL;
  737.  
  738.         exit (0);
  739. #endif /* !AMIGA */
  740.       }
  741.     }
  742.   return 0;            /* Fool -Wall . . . */
  743. }
  744.  
  745. static char **diffargv;
  746.  
  747. static void
  748. diffarg (a)
  749.      char *a;
  750. {
  751.   static unsigned diffargs, diffargsmax;
  752.  
  753.   if (diffargs == diffargsmax)
  754.     {
  755.       if (! diffargsmax)
  756.     {
  757.       diffargv = (char **) xmalloc (sizeof (char));
  758.       diffargsmax = 8;
  759.     }
  760.       diffargsmax *= 2;
  761.       diffargv = (char **) realloc (diffargv, diffargsmax * sizeof (char *));
  762.       if (! diffargv)
  763.     fatal ("out of memory");
  764.     }
  765.   diffargv[diffargs++] = a;
  766. }
  767.  
  768. #ifndef AMIGA
  769.  
  770. static void
  771. execdiff (differences_only, option, file1, file2)
  772.      int differences_only;
  773.      char *option, *file1, *file2;
  774. {
  775.   if (differences_only)
  776.     diffarg ("--suppress-common-lines");
  777.   diffarg (option);
  778.   diffarg ("--");
  779.   diffarg (file1);
  780.   diffarg (file2);
  781.   diffarg (0);
  782.  
  783.   execvp (diffbin, diffargv);
  784.   write (fileno (stderr), diffbin, strlen (diffbin));
  785.   write (fileno (stderr), ": not found\n", 12);
  786.   _exit (2);
  787. }
  788.  
  789. #else /* AMIGA */
  790.  
  791. static void
  792. execdiff (differences_only, option, file1, file2, asynch, handle)
  793.      int differences_only;
  794.      char *option, *file1, *file2;
  795.      long asynch;
  796.      BPTR handle;
  797. {
  798.   struct TagItem STags[5];
  799.  
  800.   if (differences_only)
  801.     diffarg ("--suppress-common-lines");
  802.   diffarg (option);
  803.   diffarg ("--");
  804.   diffarg (file1);
  805.   diffarg (file2);
  806.   diffarg (0);
  807.  
  808.   construct_command_line (diffbin, diffargv, command_line);
  809.   if (asynch)
  810.     {
  811.       STags[0].ti_Tag = SYS_Input;
  812.       STags[0].ti_Data = NULL;
  813.       STags[1].ti_Tag = SYS_Output;
  814.       STags[1].ti_Data = handle;
  815.       STags[2].ti_Tag = SYS_Asynch;
  816.       STags[2].ti_Data = TRUE;
  817.       STags[3].ti_Tag = SYS_UserShell;
  818.       STags[3].ti_Data = TRUE;
  819.       STags[4].ti_Tag = TAG_DONE;
  820.       if (System (command_line, STags) != 0)
  821.         perror_fatal ("diff not found");
  822.     }
  823.   else
  824.     {
  825.       STags[0].ti_Tag = SYS_Asynch;
  826.       STags[0].ti_Data = FALSE;
  827.       STags[1].ti_Tag = SYS_UserShell;
  828.       STags[1].ti_Data = TRUE;
  829.       STags[2].ti_Tag = TAG_DONE;
  830.       if (System (command_line, STags) != 0)
  831.         perror_fatal ("diff not found");
  832.       exit (0);
  833.     }
  834. }
  835.  
  836. static void
  837. construct_command_line (binname, argvec, com_line)
  838.      char *binname, **argvec, *com_line;
  839. {
  840.   int i;
  841.   strcpy (com_line, binname);
  842.   for (i = 1; argvec[i]; i++)
  843.     {
  844.       /* Enclose arguments in quotes */
  845.       strcat (com_line, " \"");
  846.       strcat (com_line, argvec[i]);
  847.       strcat (com_line, "\"");
  848.     }
  849. }
  850.  
  851. static void
  852. construct_pipe_name (pipe_name)
  853.      char *pipe_name;
  854. {
  855.   static long invocations = 0;
  856.   struct Task *Task;
  857.  
  858.   Task = FindTask (NULL);
  859.   sprintf (pipe_name, "PIPE:%08lX_%ld", Task, invocations);
  860. }
  861.  
  862. #endif /* !AMIGA */
  863.  
  864.  
  865.  
  866.  
  867. #ifndef AMIGA
  868. /* Signal handling */
  869.  
  870. static int volatile ignore_signals;
  871.  
  872. static void
  873. catchsig (s)
  874.      int s;
  875. {
  876.   signal (s, catchsig);
  877.   if (! ignore_signals)
  878.     {
  879.       cleanup ();
  880.       _exit (2);
  881.     }
  882. }
  883.  
  884. static void
  885. trapsigs ()
  886. {
  887.   static int const sigs[] = {
  888. #   ifdef SIGHUP
  889.       SIGHUP,
  890. #   endif
  891. #   ifdef SIGQUIT
  892.       SIGQUIT,
  893. #   endif
  894. #   ifdef SIGTERM
  895.       SIGTERM,
  896. #   endif
  897. #   ifdef SIGXCPU
  898.       SIGXCPU,
  899. #   endif
  900. #   ifdef SIGXFSZ
  901.       SIGXFSZ,
  902. #   endif
  903.       SIGINT,
  904.       SIGPIPE
  905.   };
  906.   int const *p;
  907.  
  908.   for (p = sigs;  p < sigs + sizeof (sigs) / sizeof (*sigs);  p++)
  909.     if (signal (*p, SIG_IGN) != SIG_IGN  &&  signal (*p, catchsig) != SIG_IGN)
  910.       fatal ("signal error");
  911. }
  912. #endif /* !AMIGA */
  913.  
  914.  
  915.  
  916. static void
  917. give_help ()
  918. {
  919.   fprintf (stderr,"l:\tuse the left version\n");
  920.   fprintf (stderr,"r:\tuse the right version\n");
  921.   fprintf (stderr,"e l:\tedit then use the left version\n");
  922.   fprintf (stderr,"e r:\tedit then use the right version\n");
  923.   fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
  924.   fprintf (stderr,"e:\tedit a new version\n");
  925.   fprintf (stderr,"s:\tsilently include common lines\n");
  926.   fprintf (stderr,"v:\tverbosely include common lines\n");
  927.   fprintf (stderr,"q:\tquit\n");
  928. }
  929.  
  930. static int
  931. skip_white ()
  932. {
  933.   int c;
  934.   while (isspace (c = getchar ()) && c != '\n')
  935.     ;
  936.   if (ferror (stdin))
  937.     perror_fatal ("input error");
  938.   return c;
  939. }
  940.  
  941. static void
  942. flush_line ()
  943. {
  944.   int c;
  945.   while ((c = getchar ()) != '\n' && c != EOF)
  946.     ;
  947.   if (ferror (stdin))
  948.     perror_fatal ("input error");
  949. }
  950.  
  951.  
  952. /* interpret an edit command */
  953. static int
  954. edit (left, lenl, right, lenr, outfile)
  955.      struct line_filter *left;
  956.      int lenl;
  957.      struct line_filter *right;
  958.      int lenr;
  959.      FILE *outfile;
  960. {
  961.   for (;;)
  962.     {
  963.       int cmd0, cmd1;
  964.       int gotcmd = 0;
  965.  
  966.       while (!gotcmd)
  967.     {
  968.       if (putchar ('%') != '%')
  969.         perror_fatal ("output error");
  970.       ck_fflush (stdout);
  971.  
  972.       cmd0 = skip_white ();
  973.       switch (cmd0)
  974.         {
  975.         case 'l': case 'r': case 's': case 'v': case 'q':
  976.           if (skip_white () != '\n')
  977.         {
  978.           give_help ();
  979.           flush_line ();
  980.           continue;
  981.         }
  982.           gotcmd = 1;
  983.           break;
  984.  
  985.         case 'e':
  986.           cmd1 = skip_white ();
  987.           switch (cmd1)
  988.         {
  989.         case 'l': case 'r': case 'b':
  990.           if (skip_white () != '\n')
  991.             {
  992.               give_help ();
  993.               flush_line ();
  994.               continue;
  995.             }
  996.           gotcmd = 1;
  997.           break;
  998.         case '\n':
  999.           gotcmd = 1;
  1000.           break;
  1001.         default:
  1002.           give_help ();
  1003.           flush_line ();
  1004.           continue;
  1005.         }
  1006.           break;
  1007.         case EOF:
  1008.           if (feof (stdin))
  1009.         {
  1010.           gotcmd = 1;
  1011.           cmd0 = 'q';
  1012.           break;
  1013.         }
  1014.           /* falls through */
  1015.         default:
  1016.           give_help ();
  1017.           flush_line ();
  1018.           continue;
  1019.         }
  1020.     }
  1021.  
  1022.       switch (cmd0)
  1023.     {
  1024.     case 'l':
  1025.       lf_copy (left, lenl, outfile);
  1026.       lf_skip (right, lenr);
  1027.       return 1;
  1028.     case 'r':
  1029.       lf_copy (right, lenr, outfile);
  1030.       lf_skip (left, lenl);
  1031.       return 1;
  1032.     case 's':
  1033.       suppress_common_flag = 1;
  1034.       break;
  1035.     case 'v':
  1036.       suppress_common_flag = 0;
  1037.       break;
  1038.     case 'q':
  1039. #ifdef AMIGA
  1040.           user_quit = 1;
  1041. #endif
  1042.       return 0;
  1043. #ifndef AMIGA
  1044.     case 'e':
  1045.       if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0)))
  1046.         perror_fatal ("temporary file name");
  1047.  
  1048.       tmpmade = 1;
  1049.  
  1050.       {
  1051.         FILE *tmp = ck_fopen (tmpname, "w+");
  1052.  
  1053.         if (cmd1 == 'l' || cmd1 == 'b')
  1054.           lf_copy (left, lenl, tmp);
  1055.         else
  1056.           lf_skip (left, lenl);
  1057.  
  1058.         if (cmd1 == 'r' || cmd1 == 'b')
  1059.           lf_copy (right, lenr, tmp);
  1060.         else
  1061.           lf_skip (right, lenr);
  1062.  
  1063.         ck_fflush (tmp);
  1064.  
  1065.         {
  1066.           pid_t pid;
  1067.           int wstatus;
  1068.  
  1069.           ignore_signals = 1;
  1070.  
  1071.           pid = vfork ();
  1072.           if (pid == 0)
  1073.         {
  1074.           char const *argv[3];
  1075.           int i = 0;
  1076.  
  1077.           argv[i++] = edbin;
  1078.           argv[i++] = tmpname;
  1079.           argv[i++] = 0;
  1080.  
  1081.           execvp (edbin, (char **) argv);
  1082.           write (fileno (stderr), edbin, strlen (edbin));
  1083.           write (fileno (stderr), ": not found\n", 12);
  1084.           _exit (1);
  1085.         }
  1086.  
  1087.           if (pid < 0)
  1088.         perror_fatal ("fork failed");
  1089.  
  1090.           while (waitpid (pid, &wstatus, 0) < 0)
  1091.         if (errno != EINTR)
  1092.           perror_fatal ("wait failed");
  1093.  
  1094.           ignore_signals = 0;
  1095.  
  1096.           if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
  1097.         fatal ("Subsidiary editor failed");
  1098.         }
  1099.  
  1100.         if (fseek (tmp, 0L, SEEK_SET) != 0)
  1101.           perror_fatal ("fseek");
  1102.         {
  1103.           char buf[SDIFF_BUFSIZE];
  1104.           size_t size;
  1105.  
  1106.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1107.         ck_fwrite (buf, size, outfile);
  1108.           ck_fclose (tmp);
  1109.         }
  1110.         return 1;
  1111.       }
  1112. #else /* AMIGA */
  1113.     case 'e':
  1114.           if (! tmpname && ! (tmpname = tmpnam (NULL)))
  1115.         perror_fatal ("temporary file name");
  1116.  
  1117.       tmpmade = 1;
  1118.  
  1119.       {
  1120.         FILE *tmp;
  1121.  
  1122.             tmp = ck_fopen (tmpname, "w");
  1123.  
  1124.         if (cmd1 == 'l' || cmd1 == 'b')
  1125.           lf_copy (left, lenl, tmp);
  1126.         else
  1127.           lf_skip (left, lenl);
  1128.  
  1129.         if (cmd1 == 'r' || cmd1 == 'b')
  1130.           lf_copy (right, lenr, tmp);
  1131.         else
  1132.           lf_skip (right, lenr);
  1133.  
  1134.         ck_fclose (tmp);
  1135.  
  1136.             {
  1137.               struct TagItem STags[3];
  1138.  
  1139.               sprintf (command_line, "%s \"%s\"", edbin, tmpname);
  1140.               STags[0].ti_Tag = SYS_Asynch;
  1141.               STags[0].ti_Data = FALSE;
  1142.               STags[1].ti_Tag = SYS_UserShell;
  1143.               STags[1].ti_Data = TRUE;
  1144.               STags[2].ti_Tag = TAG_DONE;
  1145.               if (System (command_line, STags) != 0)
  1146.                 perror_fatal ("Subsidiary editor failed");
  1147.         }
  1148.  
  1149.  
  1150.         {
  1151.           static char *buf;
  1152.           size_t size;
  1153.  
  1154.               tmp = ck_fopen (tmpname, "r");
  1155.  
  1156.               buf = xmalloc (SDIFF_BUFSIZE * sizeof (char));
  1157.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1158.         ck_fwrite (buf, size, outfile);
  1159.               free (buf);
  1160.  
  1161.           ck_fclose (tmp);
  1162.         }
  1163.         return 1;
  1164.       }
  1165. #endif /* !AMIGA */
  1166.     default:
  1167.       give_help ();
  1168.       break;
  1169.     }
  1170.     }
  1171. }
  1172.  
  1173.  
  1174.  
  1175. /* Alternately reveal bursts of diff output and handle user editing comands.  */
  1176. static int
  1177. interact (diff, left, right, outfile)
  1178.      struct line_filter *diff;
  1179.      struct line_filter *left;
  1180.      struct line_filter *right;
  1181.      FILE *outfile;
  1182. {
  1183.   for (;;)
  1184.     {
  1185.       char diff_help[256];
  1186.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  1187.  
  1188.       if (snarfed <= 0)
  1189.     return snarfed;
  1190.  
  1191.       switch (diff_help[0])
  1192.     {
  1193.     case ' ':
  1194.       puts (diff_help + 1);
  1195.       break;
  1196.     case 'i':
  1197.       {
  1198.         int lenl = atoi (diff_help + 1), lenr, lenmax;
  1199.         char *p = index (diff_help, ',');
  1200.  
  1201.         if (!p)
  1202.           fatal (diff_help);
  1203.         lenr = atoi (p + 1);
  1204.         lenmax = max (lenl, lenr);
  1205.  
  1206.         if (suppress_common_flag)
  1207.           lf_skip (diff, lenmax);
  1208.         else
  1209.           lf_copy (diff, lenmax, stdout);
  1210.  
  1211.         lf_copy (left, lenl, outfile);
  1212.         lf_skip (right, lenr);
  1213.         break;
  1214.       }
  1215.     case 'c':
  1216.       {
  1217.         int lenl = atoi (diff_help + 1), lenr;
  1218.         char *p = index (diff_help, ',');
  1219.  
  1220.         if (!p)
  1221.           fatal (diff_help);
  1222.         lenr = atoi (p + 1);
  1223.         lf_copy (diff, max (lenl, lenr), stdout);
  1224.         if (! edit (left, lenl, right, lenr, outfile))
  1225.           return 0;
  1226.         break;
  1227.       }
  1228.     default:
  1229.       fatal (diff_help);
  1230.       break;
  1231.     }
  1232.     }
  1233. }
  1234.  
  1235.  
  1236.  
  1237. /* temporary lossage: this is torn from gnu libc */
  1238. /* Return nonzero if DIR is an existent directory.  */
  1239. static int
  1240. diraccess (dir)
  1241.      const char *dir;
  1242. {
  1243.   struct stat buf;
  1244.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1245. }
  1246.  
  1247. /* Return nonzero if FILE exists.  */
  1248. static int
  1249. exists (file)
  1250.      const char *file;
  1251. {
  1252.   struct stat buf;
  1253.   return stat (file, &buf) == 0;
  1254. }
  1255.  
  1256. #ifndef AMIGA
  1257.  
  1258. /* These are the characters used in temporary filenames.  */
  1259. static const char letters[] =
  1260.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1261.  
  1262. /* Generate a temporary filename.
  1263.    If DIR_SEARCH is nonzero, DIR and PFX are used as
  1264.    described for tempnam.  If not, a temporary filename
  1265.    in P_tmpdir with no special prefix is generated.  If LENPTR
  1266.    is not NULL, *LENPTR is set the to length (including the
  1267.    terminating '\0') of the resultant filename, which is returned.
  1268.    This goes through a cyclic pattern of all possible filenames
  1269.    consisting of five decimal digits of the current pid and three
  1270.    of the characters in `letters'.  Data for tempnam and tmpnam
  1271.    is kept separate, but when tempnam is using P_tmpdir and no
  1272.    prefix (i.e, it is identical to tmpnam), the same data is used.
  1273.    Each potential filename is tested for an already-existing file of
  1274.    the same name, and no name of an existing file will be returned.
  1275.    When the cycle reaches its end (12345ZZZ), NULL is returned.  */
  1276.  
  1277.  
  1278. static char *
  1279. private_tempnam (dir, pfx, dir_search, lenptr)
  1280.      const char *dir;
  1281.      const char *pfx;
  1282.      int dir_search;
  1283.      size_t *lenptr;
  1284. {
  1285.   static const char tmpdir[] = PVT_tmpdir;
  1286.   static struct
  1287.     {
  1288.       char buf[3];
  1289.       char *s;
  1290.       size_t i;
  1291.     } infos[2], *info;
  1292.   static char buf[TMPNAMSIZE];
  1293.   static pid_t oldpid = 0;
  1294.   pid_t pid = getpid ();
  1295.   register size_t len, plen;
  1296.  
  1297.   if (dir_search)
  1298.     {
  1299.       register const char *d = getenv ("TMPDIR");
  1300.       if (d != NULL && !diraccess (d))
  1301.     d = NULL;
  1302.       if (d == NULL && dir != NULL && diraccess (dir))
  1303.     d = dir;
  1304.       if (d == NULL && diraccess (tmpdir))
  1305.     d = tmpdir;
  1306.       if (d == NULL && diraccess ("/tmp"))
  1307.     d = "/tmp";
  1308.       if (d == NULL)
  1309.     {
  1310.       errno = ENOENT;
  1311.       return NULL;
  1312.     }
  1313.       dir = d;
  1314.     }
  1315.   else
  1316.     dir = tmpdir;
  1317.  
  1318.   if (pfx != NULL && *pfx != '\0')
  1319.     {
  1320.       plen = strlen (pfx);
  1321.       if (plen > 5)
  1322.     plen = 5;
  1323.     }
  1324.   else
  1325.     plen = 0;
  1326.  
  1327.   if (dir != tmpdir && !strcmp (dir, tmpdir))
  1328.     dir = tmpdir;
  1329.   info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
  1330.  
  1331.   if (pid != oldpid)
  1332.     {
  1333.       oldpid = pid;
  1334.       info->buf[0] = info->buf[1] = info->buf[2] = '0';
  1335.       info->s = &info->buf[0];
  1336.       info->i = 0;
  1337.     }
  1338.  
  1339.   len = strlen (dir) + 1 + plen + 8;
  1340.   for (;;)
  1341.     {
  1342.       *info->s = letters[info->i];
  1343.       sprintf (buf, "%s/%.*s%.5d%.3s", dir, (int) plen, pfx,
  1344.           pid % 100000, info->buf);
  1345.       if (!exists (buf))
  1346.     break;
  1347.       ++info->i;
  1348.       if (info->i > sizeof (letters) - 1)
  1349.     {
  1350.       info->i = 0;
  1351.       if (info->s == &info->buf[2])
  1352.         {
  1353.           errno = EEXIST;
  1354.           return NULL;
  1355.         }
  1356.       ++info->s;
  1357.     }
  1358.     }
  1359.  
  1360.   if (lenptr != NULL)
  1361.     *lenptr = len;
  1362.   return buf;
  1363. }
  1364.  
  1365. #endif /* AMIGA */
  1366.